Skip to content

(MOT-4106) feat(github): GitHub CLI (gh) as an iii worker#529

Merged
andersonleal merged 4 commits into
mainfrom
feat/gh-worker
Jul 20, 2026
Merged

(MOT-4106) feat(github): GitHub CLI (gh) as an iii worker#529
andersonleal merged 4 commits into
mainfrom
feat/gh-worker

Conversation

@andersonleal

@andersonleal andersonleal commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

What

New standalone github worker (Rust, deploy: binary, v0.1.0) wrapping the GitHub CLI as iii functions:

  • 30 typed functions across the high-traffic groups — github::pr::* (list/view/create/edit/merge/comment/review/diff/checks), github::issue::* (list/view/create/edit/comment/close), github::repo::* (view/list), github::run::* (list/view/rerun/cancel), github::workflow::* (list/run), github::release::* (list/view/create), github::search::* (repos/issues/prs/code).
  • Two escape hatches keeping 100% of gh reachable: github::exec (argv passthrough; non-zero exit and timeout are DATA — {stdout, stderr, exit_code, timed_out, *_truncated}) and github::api (any REST endpoint, -f fields / JSON body via --input - / --jq / --paginate).

Design

  • Exec core lifts shell/src/exec/host.rs mechanics: bounded capture that keeps draining after the cap (SIGPIPE guard preserves real exit codes), process-group SIGKILL on timeout, kill_on_drop, stdin piped only when supplied. Machine-branchable failure codes via Error::Remote (gh_not_found, spawn_failed, timeout).
  • Curated fns: pure request -> argv builders (unit-tested without spawning), -R owner/name always explicit (the worker has no checkout; repo view/list are positional), non-zero exits become Error::Handler carrying gh's stderr — except pr::checks, where exit codes {0,1,8} are check-state data.
  • Config via the configuration worker (devin pattern): gh_executable PATH fallback, token: "${GH_TOKEN}" injected into child env with ambient gh auth login fallback, timeout clamp (30s default / 120s max), 1 MiB per-stream caps. No gh probe at boot — interface collection works on bare runners.
  • Wire surface golden-tested (context-manager harness): 32 committed schema snapshots, typed request+response gate, registration-order pin.
  • Permissions: 17 read-only fns allowed; mutations + both escape hatches stay needs_approval; github::on-config-change denied.

Verification

  • cargo fmt --check, clippy -D warnings, cargo test (32 tests) all green.
  • Live-smoked against a local engine with gh 2.94: all 32 functions register; exec --version, repo::view cli/cli, pr::list, pr::checks (pending/failed as data), api rate_limit + --jq string fallback, timeout-as-data, and the 404 error path all behave as designed.

Wiring

create-tag.yml worker option, release.yml tag glob, root README Modules row, root iii-permissions.yaml rules. First release: Create Tag with worker=github, registry tag next.

Summary by CodeRabbit

  • New Features
    • Added a new GitHub worker with typed tools for pull requests, issues, repositories, Actions, releases, and search, plus command execution and REST API helpers.
    • Added a dedicated GitHub page in the console with PRs, Issues, Runs, Releases, and Search panels (including availability-based navigation).
  • Documentation
    • Documented the GitHub worker, configuration options, and skill boundaries; updated the README module list.
  • Chores
    • Updated workflow triggers to support additional tag patterns and manual worker selection.

30 typed github::pr/issue/repo/run/workflow/release/search functions plus
github::exec (argv passthrough, outcome as data) and github::api (any REST
endpoint). Bounded-capture exec core with process-group timeout kill
(shell's run_to_completion mechanics), GH_TOKEN via the configuration
worker with ambient gh-auth fallback, golden-tested wire schemas, and
read-only functions allowed in iii-permissions (mutations and both escape
hatches stay approval-gated).
@vercel

vercel Bot commented Jul 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
workers Ready Ready Preview, Comment Jul 20, 2026 6:32pm
workers-tech-spec Ready Ready Preview, Comment Jul 20, 2026 6:32pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

skill-check — worker

0 verified, 47 skipped (no docs/).

Layer Result
structure
vale
ai
render

Four for four. Nicely done.

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@andersonleal, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 126429e0-f4ef-4e33-b62d-23158f820e52

📥 Commits

Reviewing files that changed from the base of the PR and between b0f1ff8 and 66b24a6.

📒 Files selected for processing (1)
  • github/iii.worker.yaml
📝 Walkthrough

Walkthrough

Adds a Rust-based GitHub worker backed by the gh CLI and a routed console view. It provides typed GitHub operations, hot-reloaded configuration, bounded execution, permissions, schemas, tests, documentation, and CI release wiring.

Changes

GitHub worker

Layer / File(s) Summary
Worker packaging and bootstrap
.github/workflows/*, README.md, github/Cargo.toml, github/*.yaml, github/README.md, github/skills/*, github/src/main.rs
Adds worker packaging, installation metadata, configuration seeds, permissions, documentation, workflow options, release tag triggers, and binary startup wiring.
Runtime configuration and gh execution
github/src/config.rs, github/src/configuration.rs, github/src/gh.rs, github/src/lib.rs, github/tests/contract.rs
Adds environment-expanded configuration, hot reload reconciliation, bounded gh execution, timeout and process-group handling, token injection, and runtime contract tests.
Typed GitHub command contracts
github/src/functions/actions.rs, issue.rs, pr.rs, release.rs, repo.rs, search.rs, passthrough.rs
Adds typed request structures, command metadata, deterministic CLI argument builders, escape hatches, and unit tests for GitHub operations.
Function registration and schema validation
github/src/functions/mod.rs, github/tests/schemas.rs, github/tests/support/*, github/tests/golden/schemas/*
Registers typed functions, maps command results into typed responses, generates the ordered schema catalog, and validates golden contracts.
Console GitHub view
console/web/src/App.tsx, console/web/src/hooks/*, console/web/src/lib/github*, console/web/src/pages/Github/*
Adds GitHub worker availability gating, hash routing, typed RPC parsing, query management, repository persistence, and panels for PRs, issues, runs, releases, and search.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Poem

A rabbit hops through GitHub’s gate,
With typed commands neatly laid in state.
gh runs swift, configs softly reload,
Schemas watch each contract code.
PRs and issues dance in line—
“Ship the worker!” squeaks the bunny fine.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.46% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: introducing a GitHub CLI worker for iii.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/gh-worker

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@andersonleal andersonleal changed the title feat(github): GitHub CLI (gh) as an iii worker (MOT-4106) feat(github): GitHub CLI (gh) as an iii worker Jul 18, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@github/src/functions/pr.rs`:
- Around line 150-163: Update CreateRequest so head is a required String rather
than an Option, then adjust the PR creation flow to always pass the request’s
head value to gh pr create via --head. Ensure all construction and handling of
CreateRequest provide and propagate head explicitly.

In `@github/src/functions/search.rs`:
- Around line 45-48: Update search_args to insert the argument separator "--"
before query, ensuring leading-negative queries are treated as search terms
rather than flags while preserving the existing kind, JSON, and limit arguments.

In `@github/src/gh.rs`:
- Around line 103-125: Update the timeout branch around kill_process_group and
child.start_kill to terminate the entire gh process tree on Windows, using a
Windows job object or equivalent descendant cleanup before awaiting stdout_task
and stderr_task. Preserve existing behavior on Unix and non-Windows platforms,
and add a test covering a spawned descendant that keeps stdout or stderr open
past the timeout.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d42fefe2-d135-4f07-9317-9c9803adb7da

📥 Commits

Reviewing files that changed from the base of the PR and between 3a5401d and 20cdd01.

⛔ Files ignored due to path filters (1)
  • github/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (58)
  • .github/workflows/create-tag.yml
  • .github/workflows/release.yml
  • README.md
  • github/Cargo.toml
  • github/README.md
  • github/config.yaml
  • github/iii-permissions.yaml
  • github/iii.worker.yaml
  • github/skills/SKILL.md
  • github/src/config.rs
  • github/src/configuration.rs
  • github/src/functions/actions.rs
  • github/src/functions/issue.rs
  • github/src/functions/mod.rs
  • github/src/functions/passthrough.rs
  • github/src/functions/pr.rs
  • github/src/functions/release.rs
  • github/src/functions/repo.rs
  • github/src/functions/search.rs
  • github/src/gh.rs
  • github/src/lib.rs
  • github/src/main.rs
  • github/tests/contract.rs
  • github/tests/golden/schemas/github.api.json
  • github/tests/golden/schemas/github.exec.json
  • github/tests/golden/schemas/github.issue.close.json
  • github/tests/golden/schemas/github.issue.comment.json
  • github/tests/golden/schemas/github.issue.create.json
  • github/tests/golden/schemas/github.issue.edit.json
  • github/tests/golden/schemas/github.issue.list.json
  • github/tests/golden/schemas/github.issue.view.json
  • github/tests/golden/schemas/github.pr.checks.json
  • github/tests/golden/schemas/github.pr.comment.json
  • github/tests/golden/schemas/github.pr.create.json
  • github/tests/golden/schemas/github.pr.diff.json
  • github/tests/golden/schemas/github.pr.edit.json
  • github/tests/golden/schemas/github.pr.list.json
  • github/tests/golden/schemas/github.pr.merge.json
  • github/tests/golden/schemas/github.pr.review.json
  • github/tests/golden/schemas/github.pr.view.json
  • github/tests/golden/schemas/github.release.create.json
  • github/tests/golden/schemas/github.release.list.json
  • github/tests/golden/schemas/github.release.view.json
  • github/tests/golden/schemas/github.repo.list.json
  • github/tests/golden/schemas/github.repo.view.json
  • github/tests/golden/schemas/github.run.cancel.json
  • github/tests/golden/schemas/github.run.list.json
  • github/tests/golden/schemas/github.run.rerun.json
  • github/tests/golden/schemas/github.run.view.json
  • github/tests/golden/schemas/github.search.code.json
  • github/tests/golden/schemas/github.search.issues.json
  • github/tests/golden/schemas/github.search.prs.json
  • github/tests/golden/schemas/github.search.repos.json
  • github/tests/golden/schemas/github.workflow.list.json
  • github/tests/golden/schemas/github.workflow.run.json
  • github/tests/schemas.rs
  • github/tests/support/mod.rs
  • iii-permissions.yaml

Comment thread github/src/functions/pr.rs
Comment thread github/src/functions/search.rs
Comment thread github/src/gh.rs
…b page

Five read-only panels over the github worker: pull requests (state filter +
inline CI checks rollup via github::pr::checks), issues, Actions runs,
releases, and org-wide search (repos/issues/prs/code). Repo input persists
in localStorage; data loads on demand (no polling — gh rate limits). Nav
entry gated on worker presence like worktrees/browser/memory; mutations
deliberately stay in agent flows behind the approval gate. Console bumped
to 1.6.11.
…ch queries

Review follow-ups: `github::pr::create` now requires `head` (the worker runs
outside any checkout, so gh cannot infer a branch — schema-required beats a
runtime gh error), and `gh search` argv puts flags before `--` with the query
positional after it, so leading negative qualifiers ("-label:bug") stop being
swallowed as flags (verified against gh 2.94).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@console/web/src/pages/Github/index.tsx`:
- Around line 46-144: Replace the bump-based remount in the Github page with a
refreshToken prop threaded into PrsPanel, IssuesPanel, RunsPanel, ReleasesPanel,
and SearchPanel. Update each panel’s fetcher callback dependencies to include
refreshToken so useGithubQuery refetches while preserving panel-local filters
and expansion state; keep repository and panel changes functioning normally
without using refreshToken in the container key.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a535744e-c458-4704-b4a2-58693fbb7b4b

📥 Commits

Reviewing files that changed from the base of the PR and between 20cdd01 and b0f1ff8.

⛔ Files ignored due to path filters (1)
  • console/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • console/Cargo.toml
  • console/web/src/App.tsx
  • console/web/src/hooks/use-github-status.test.ts
  • console/web/src/hooks/use-github-status.ts
  • console/web/src/hooks/use-hash-route.ts
  • console/web/src/lib/conversations-context.tsx
  • console/web/src/lib/github.test.ts
  • console/web/src/lib/github.ts
  • console/web/src/lib/nav-options.test.ts
  • console/web/src/lib/nav-options.ts
  • console/web/src/pages/Github/components/IssuesPanel.tsx
  • console/web/src/pages/Github/components/PanelShell.tsx
  • console/web/src/pages/Github/components/PrsPanel.tsx
  • console/web/src/pages/Github/components/ReleasesPanel.tsx
  • console/web/src/pages/Github/components/RunsPanel.tsx
  • console/web/src/pages/Github/components/SearchPanel.tsx
  • console/web/src/pages/Github/hooks/useGithubQuery.ts
  • console/web/src/pages/Github/index.tsx
  • github/src/functions/pr.rs
  • github/src/functions/search.rs
  • github/tests/golden/schemas/github.pr.create.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • github/tests/golden/schemas/github.pr.create.json
  • github/src/functions/search.rs
  • github/src/functions/pr.rs

Comment on lines +46 to +144
const [bump, setBump] = useState(0)

const commitRepo = () => {
const next = repoInput.trim()
setRepo(next)
saveGithubRepo(next)
}

const needsRepo = panel !== 'search'

return (
<main
className="flex-1 flex flex-col min-h-0 overflow-hidden"
aria-label="github"
>
<header className="shrink-0 px-4 sm:px-6 lg:px-8 py-4 border-b border-rule flex flex-wrap items-center justify-between gap-3">
<div>
<h1 className="font-mono text-[16px] font-semibold tracking-[-0.01em] text-ink lowercase">
github
</h1>
<p className="font-mono text-[12px] text-ink-faint mt-0.5 lowercase">
{available
? repo || 'no repository selected'
: 'worker not connected'}
</p>
</div>
{available ? (
<Button
variant="ghost"
size="sm"
onClick={() => setBump((b) => b + 1)}
className="gap-1.5"
>
<RefreshCw className="w-3.5 h-3.5" aria-hidden />
refresh
</Button>
) : null}
</header>

{available ? (
<div className="shrink-0 px-4 sm:px-6 lg:px-8 py-3 border-b border-rule flex flex-wrap items-center gap-3">
{needsRepo ? (
<Input
value={repoInput}
onChange={setRepoInput}
onBlur={commitRepo}
onKeyDown={(e) => {
if (e.key === 'Enter') commitRepo()
}}
placeholder="owner/name"
preserveCase
aria-label="repository"
className="w-64 max-w-full"
/>
) : null}
<ModeToggle<Panel>
value={panel}
onChange={setPanel}
options={PANEL_OPTIONS}
/>
</div>
) : null}

<div className="flex-1 overflow-auto px-4 sm:px-6 lg:px-8 py-4">
{!available ? (
status.loading ? (
<p className="font-mono text-[12px] lowercase text-ink-ghost">
checking for the github worker...
</p>
) : (
<StatusPanel
variant="info"
icon={<AlertCircle className="w-full h-full" />}
headline="github worker not installed"
detail="this page needs the optional github worker (and the gh CLI on its host). run: iii worker add github"
/>
)
) : needsRepo && repo === '' ? (
<EmptyState
icon={FolderGit2}
title="no repository selected"
description="enter a repository above as owner/name — e.g. iii-hq/workers — and press enter. the search panel works without one."
/>
) : (
// Remount on repo/panel/refresh so every fetch hook restarts clean.
<div key={`${panel}:${repo}:${bump}`}>
{panel === 'prs' ? (
<PrsPanel repo={repo} enabled={available} />
) : panel === 'issues' ? (
<IssuesPanel repo={repo} enabled={available} />
) : panel === 'runs' ? (
<RunsPanel repo={repo} enabled={available} />
) : panel === 'releases' ? (
<ReleasesPanel repo={repo} enabled={available} />
) : (
<SearchPanel enabled={available} />
)}
</div>
)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Refresh resets panel-local filters/expansion instead of just refetching.

bump is folded into the remount key (Line 131), so clicking refresh fully unmounts/remounts the active panel. That wipes local UI state each panel keeps for itself — e.g. IssuesPanel's open/closed state filter, PrsPanel's state filter and expanded-checks row — silently reverting them to defaults. useGithubQuery already exposes a non-destructive refresh() (useGithubQuery.ts Lines 15, 27) for exactly this purpose, but no panel consumes it.

Threading a refreshToken prop into each panel's fetcher deps (instead of remounting) would refetch without discarding filter/expansion state.

♻️ Sketch of a non-destructive refresh
-  const [bump, setBump] = useState(0)
+  const [refreshToken, setRefreshToken] = useState(0)
@@
-            onClick={() => setBump((b) => b + 1)}
+            onClick={() => setRefreshToken((t) => t + 1)}
@@
-          <div key={`${panel}:${repo}:${bump}`}>
+          <div key={`${panel}:${repo}`}>
             {panel === 'prs' ? (
-              <PrsPanel repo={repo} enabled={available} />
+              <PrsPanel repo={repo} enabled={available} refreshToken={refreshToken} />

Each panel would then add refreshToken to its fetcher's useCallback deps (e.g. PrsPanel.tsx Line 54) so useGithubQuery refetches without unmounting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@console/web/src/pages/Github/index.tsx` around lines 46 - 144, Replace the
bump-based remount in the Github page with a refreshToken prop threaded into
PrsPanel, IssuesPanel, RunsPanel, ReleasesPanel, and SearchPanel. Update each
panel’s fetcher callback dependencies to include refreshToken so useGithubQuery
refetches while preserving panel-local filters and expansion state; keep
repository and panel changes functioning normally without using refreshToken in
the container key.

@andersonleal
andersonleal merged commit a0ed4d0 into main Jul 20, 2026
17 checks passed
@andersonleal
andersonleal deleted the feat/gh-worker branch July 20, 2026 18:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants